package com.kof.lock.core.redisSharedLock.lock.handler;

import com.kof.lock.core.redisSharedLock.RedisClient;
import com.kof.lock.core.redisSharedLock.lock.RedisLock;
import com.kof.lock.core.redisSharedLock.lock.annotion.ExclusiveLock;
import com.kof.lock.core.redisSharedLock.lock.annotion.LockKeyElement;
import com.kof.lock.core.redisSharedLock.lock.annotion.SharedLock;
import com.kof.lock.core.redisSharedLock.lock.exception.LockException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

@Aspect
@Component
public class ExclusiveLockAspect  {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExclusiveLockAspect.class);

    @Autowired
    private RedisClient redisClient;

    @Pointcut("@annotation(com.kof.lock.core.redisSharedLock.lock.annotion.ExclusiveLock)")
    public void lockPoint(){

    }

    @Around("lockPoint()")
    public Object aroundLock(ProceedingJoinPoint jp)throws Throwable{

        MethodSignature signature = (MethodSignature) jp.getSignature();
        /**  todo : 获取锁的前缀；即为功能描述的前缀*/
        ExclusiveLock sharedLock = signature.getMethod().getAnnotation(ExclusiveLock.class);
        String lockPrifex = getLockPrifex(sharedLock);

        /** todo 生成唯一标识的key*/
        Object[] args = jp.getArgs();
        String key = createRedisLockKey(signature,lockPrifex,args);

        /** todo ：： 排他锁的实现 */
        return RequiredLock(jp, sharedLock, key);

    }

    private Object RequiredLock(ProceedingJoinPoint jp, ExclusiveLock sharedLock, String key) throws Throwable {
        RedisLock redisLock = RedisLock.build(redisClient, key,sharedLock.maxLockTime());

        try{
            boolean l = redisLock.lock();
            if(l){
                try {
                    Object proceed = jp.proceed();
                    return proceed;
                } catch (Throwable throwable) {
                    LOGGER.error("排他锁锁定方法报错" + throwable.getMessage(),throwable);
                    throw throwable;
                }
            }
        }finally {
            redisLock.unlock();
        }
        return null;
    }

    private String createRedisLockKey(MethodSignature signature, String lockPrifex, Object[] args) throws IllegalAccessException {

        StringBuilder builder=new StringBuilder(lockPrifex);

        for( Object MethodArg : args) {
            Class<?> MethodArgClass = MethodArg.getClass();

            if (MethodArgClass.isPrimitive()|| MethodArg instanceof String){
                buildPrimitiveAndStringArgs(signature, builder, MethodArg);
            }

            /**  todo : 参数是一个对象的时候 ，如果参数上添加了注解 表示经参数所有的属性当做key，优先级最高 ：
             *   todo : 假如只有类的属性带有注解 只获取对应属性的值作为key*/
            /** 1 */
            Parameter[] parameters = signature.getMethod().getParameters();
            String allField2LockKeyElement=null;
            boolean allField2LockKeyFlag=false;
            for (Parameter parameter :parameters){
                LockKeyElement annotation = parameter.getAnnotation(LockKeyElement.class);
                if (annotation!=null){
                    allField2LockKeyElement = annotation.element();
                    allField2LockKeyFlag=true;
                }
            }
            Field[] fieldFirsts= MethodArgClass.getFields();
            if (allField2LockKeyFlag) {
                for (Field fieldFirst : fieldFirsts) {
                    Class<? extends Field> FieldClass = fieldFirst.getClass();
                    Object fieldObjectFirst = fieldFirst.get(MethodArg);
                    if (FieldClass.isPrimitive() || fieldObjectFirst instanceof String) {

                        builder.append(allField2LockKeyElement).append("_").append(fieldObjectFirst.toString());
                    } else {
                        Class<?> fieldObjectClassSecond = fieldFirst.getClass();
                        Field[] fields2 = fieldObjectClassSecond.getFields();
                        for (Field field2 : fields2) { /** 只解析两层结构的对象 ：*/
                            Class<? extends Field> field2Class = field2.getClass();
                            Object fieldObjectSecond = field2.get(fieldObjectFirst);
                            if (field2Class.isPrimitive() || fieldObjectSecond instanceof String) {
                                Object o2 = field2.get(fieldObjectSecond);
                                builder.append(allField2LockKeyElement).append("_").append(o2.toString());
                            } else {
                                throw new LockException("参数对象过于复杂 ，不方便解析，对参数对象做调整");
                            }
                        }
                    }
                }
            }else {
                for (Field field : fieldFirsts) {
                    LockKeyElement annotation = field.getAnnotation(LockKeyElement.class);
                    if (annotation==null){
                        continue;
                    }
                    String element = annotation.element();
                    Object FeildObjectWithLockKeyElement = field.get(MethodArg);
                    if (FeildObjectWithLockKeyElement.getClass().isPrimitive() || FeildObjectWithLockKeyElement instanceof String) {
                        builder.append(element).append("_").append(FeildObjectWithLockKeyElement.toString());
                    } else {
                        Field[] fields2 = FeildObjectWithLockKeyElement.getClass().getFields();
                        for (Field field2 : fields2) {
                            LockKeyElement annotation2 = field2.getAnnotation(LockKeyElement.class);
                            if (annotation2==null){
                                continue;
                            }
                            String element2 = annotation2.element();
                            Object FeildObjectWithLockKeyElementSecond = field.get(FeildObjectWithLockKeyElement);
                            if (FeildObjectWithLockKeyElementSecond.getClass().isPrimitive() || FeildObjectWithLockKeyElementSecond instanceof String) {
                                builder.append(element2).append("_").append(FeildObjectWithLockKeyElementSecond.toString());
                            } else {
                                throw new LockException("this object is not support “LockKeyElement” ");
                            }
                        }
                    }
                }
            }
        }
        return builder.toString();
    }
    /** todo ：参数类型是字符串或者基本类型的时候， 注解添加在 参数上 */
    private StringBuilder  buildPrimitiveAndStringArgs(MethodSignature signature, StringBuilder builder, Object o) {
        Parameter[] parameters = signature.getMethod().getParameters();
        for (Parameter parameter :parameters){
            LockKeyElement annotation = parameter.getAnnotation(LockKeyElement.class);
            if (annotation!=null){
                String element = annotation.element();
                builder.append(element).append("_").append(o);
            }
        }
        return builder;
    }

    private String getLockKey(MethodSignature signature, String lockPrifex) {
        String key=null;
        List<String> NoOrderlockKeys=new ArrayList<>();
        Type[] genericParameterTypes = signature.getMethod().getGenericParameterTypes();
        for (int i=0; i<genericParameterTypes.length;i++){
            Type genericParameterType = genericParameterTypes[i];
            LockKeyElement annotation = genericParameterType.getClass().getAnnotation(LockKeyElement.class);
            String lockKey="";
            String element = annotation.element();
            String typeName= genericParameterType.getTypeName();
            if (StringUtils.isEmpty(element)){
                lockKey=typeName;
            }else {
                lockKey=element;
            }
            int elementOrder = annotation.elementOrder();
            if (elementOrder==-1){
                NoOrderlockKeys.add(lockKey);
            }else {
                // TODO: 2018/11/2   对key 的排序处理  ？？？？？
                NoOrderlockKeys.add(lockKey);
            }
        }
        StringBuilder builder=new StringBuilder(lockPrifex);
        NoOrderlockKeys.stream().forEach(x->{
            builder.append("_").append(x);
        });

        key = builder.toString();
        return key;
    }

    private String getLockPrifex(ExclusiveLock lock) {
        String lockPrifex = lock.lockPrifex();
        if (StringUtils.isEmpty(lockPrifex)){
            throw  new LockException("缺少key的前缀字符串。。。。。。");
        }
        return lockPrifex;
    }

}
